package org.example.learn.javassist.annotation;

import javassist.*;
import javassist.bytecode.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 使用javassist API 读取 类或方法上的注解
 */
public class ReadAnnotation extends ClassLoader {

    public static void main(String[] args) throws NotFoundException, ClassNotFoundException, BadBytecode, CannotCompileException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, IOException {
        ClassPool pool = ClassPool.getDefault();

        // 获取类型
        CtClass ctClass = pool.get(TestE.class.getName());

        // 获取 类 上的注解信息
        System.out.println("已知注解类型读取：");
        ClassAnnotation classAnnotation = (ClassAnnotation) ctClass.getAnnotation(ClassAnnotation.class);
        System.out.println("ClassAnnotation.ClassDeclare："+classAnnotation.ClassDeclare());
        System.out.println("ClassAnnotation.alias："+classAnnotation.alias());
        System.out.println("ClassAnnotation.timeout：" + classAnnotation.timeout());

        System.out.println("未知注解类型通过判断类型读取：");
        Object[] annotations = ctClass.getAnnotations();
        for (Object obj: annotations) {
            if (obj instanceof ClassAnnotation){
                ClassAnnotation classAnnotation1 = (ClassAnnotation) obj;
                System.out.println("ClassAnnotation.ClassDeclare：" + classAnnotation1.ClassDeclare());
                System.out.println("ClassAnnotation.alias：" + classAnnotation1.alias());
                System.out.println("ClassAnnotation.timeout：" + classAnnotation1.timeout());
            } else {
                System.out.println("未知注解：" + obj);
            }
        }

        // 获取方法上的注解
        CtMethod ctMethod = ctClass.getDeclaredMethod("testMethod");
        System.out.println("读取方法上的注解信息：");
        MethodAnnotation methodAnnotation = (MethodAnnotation) ctMethod.getAnnotation(MethodAnnotation.class);
        System.out.println("MethodAnnotation.name：" + methodAnnotation.name());
        System.out.println("MethodAnnotation.api：" + methodAnnotation.api());
        System.out.println("MethodAnnotation.type：" + methodAnnotation.type());
        // 读取方法上所有的注解 Object[] annotations = ctMethod.getAnnotations();

        System.out.println("读取方法的指令码：");
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        CodeIterator codeIterator = codeAttribute.iterator();
        while (codeIterator.hasNext()){
            int idx = codeIterator.next();
            int code = codeIterator.byteAt(idx);
            System.out.println("指令码：" + idx + " > " + Mnemonic.OPCODE[code]);
        }

//        System.out.println("原方法执行效果：");
//        byte[] bytes = ctClass.toBytecode();
//        Class<?> clazz = new ReadAnnotation().defineClass("org.example.learn.javassist.annotation.TestE",bytes,0,bytes.length);
//        Object object = clazz.newInstance();
//        Method method = clazz.getDeclaredMethod("testMethod",String.class);
//        Object val = method.invoke(object,"123");
//        System.out.println("执行后的值：" + val);

        System.out.println("通过指令码修改方法：");
        ConstPool constPool = methodInfo.getConstPool();
        Bytecode bytecode = new Bytecode(constPool);
        bytecode.addDconst(0); //将一个double类型的值压入栈顶
        bytecode.addReturn(CtClass.doubleType); // 返回一个double类型值
        //重置方法的指令码
        methodInfo.setCodeAttribute(bytecode.toCodeAttribute());

        // 输出字节码
        ctClass.writeFile();

//        System.out.println("执行修改后的方法：");
//        Class<?> newClass = ctClass.toClass();
//        Method newMethod = newClass.getMethod("testMethod",String.class);
//        Object newVal = newMethod.invoke(newClass.newInstance(),"123");
//        System.out.println("执行后的值：" + newVal);

    }
}
